#ifndef LW_DYNAMIC_ARRAY_H
#define LW_DYNAMIC_ARRAY_H

#include <string>

/**
 * 1D Array.
 * Size specified at runtime.
 */
template<typename T>
class DynamicArray {
protected:
	T* a;
	const int size;
	static const std::string indexOutOfBoundsMessage;
	static const std::string sizeMismatchMessage;

public:
	/**
	 * Creates an array with the specified size.
	 */
	DynamicArray(int size) : size(size) {
	    this->a = new T[size];
	}

	/**
	 * Creates this array as a copy of the specified array.
	 */
	DynamicArray(const DynamicArray<T>& v) : size(size) {
		if(this != &v) {
			this->size = v.size;
			this->a = new T[size];

			for(int i=0; i<size; ++i) {
				this->a[i] = v.a[i];
			}
		}
	}

	/**
	 * Deletes this array and all internal created data.
	 */
	virtual ~DynamicArray() {
		delete[] a;
	}

	/**
	 * Returns the size of this array.
	 */
	inline int Size() {
		return size;
	}

	/**
	 * Returns the value referenced by the specified index.
	 */
    inline const T& operator[](int index) const {
        if (index >= 0 && index < size) {
            return a[index];
        }

        throw std::out_of_range(indexOutOfBoundsMessage);
    }

	/**
	 * Returns or sets the value referenced by the specified index.
	 */
	inline T& operator[](int index) {
		if (index >= 0 && index < size) {
			return a[index];
		}

		throw std::out_of_range(indexOutOfBoundsMessage);
	}

	/**
	 * Compares this array with the specified array, value by value.
	 * Returns true if equal.
	 */
	bool operator==(const DynamicArray<T>& v) const {
		if(this->size == v.size) {
			for(int i=0; i<size; ++i) {
				if(this->a[i] != v.a[i]) {
					return false;
				}
			}
			return true;
		} else {
			return false;
		}
	}

	/**
	 * Compares this array with the specified array, value by value.
	 * Returns true if not equal.
	 */
	bool operator!=(const DynamicArray<T>& v) const {
		if(this->size == v.size) {
			for(int i=0; i<size; ++i) {
				if(this->a[i] != v.a[i]) {
					return true;
				}
			}
			return false;
		} else {
			return true;
		}
	}

	/**
	 * Assignes the specified array to this array, value by value.
	 * Throws exception if the sizes of the arrays are not equal.
	 */
	DynamicArray<T>& operator=(const DynamicArray<T>& v) {
		if(this != &v) {
			if(this->size != v.size) {
				throw std::range_error(sizeMismatchMessage);
			}

			for(int i=0; i<size; ++i) {
				this->a[i] = v.a[i];
			}
		}

		return *this;
	}
};

template<typename T>
const std::string DynamicArray<T>::indexOutOfBoundsMessage = "Array index out of range";

template<typename T>
const std::string DynamicArray<T>::sizeMismatchMessage = "Array size mismatch";

#endif //LW_DYNAMIC_ARRAY_H
